package com.gitee.wsl.ext.collection

import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract


fun <T> Iterable<T>.withReplacedAt(i: Int, block: (T) -> T): List<T> = take(i) + block(elementAt(i)) + drop(i + 1)

fun <T> Iterable<T>.withReplaced(t: T, block: (T) -> T): List<T> = withReplacedAt(indexOf(t), block)

/**
 * @return a new list, where each item that matches [predicate] is replaced with [with]
 */
 inline fun <T> Iterable<T>.replaceIf(with: T, predicate: (T) -> Boolean): List<T> =
    map { if (predicate(it)) with else it }

/**
 * @param apply a function that is applied on each item that matches [predicate]
 * @return a new list, where each item that matches [predicate] is replaced with the result of applying [apply] to it
 */
 inline fun <T> Iterable<T>.replaceIf(apply: T.() -> T, predicate: (T) -> Boolean): List<T> =
    map { if (predicate(it)) apply(it) else it }

/**
 * Replaces all values of [this] with values from [src]
 */
 fun <T> MutableCollection<T>.replaceWith(src: Collection<T>) {
    clear()
    addAll(src)
}


/**
 * Tries to replace [item] with [replacement] and does nothing if [item] is not found in [this]
 * @returns a list of items with [item] swapped with [replacement]
 */
@OptIn(ExperimentalContracts::class)
inline fun <T> Iterable<T>.tryReplace(
    item: T,
    replacement: T.() -> T
): List<T> {
    contract {
        callsInPlace(replacement, InvocationKind.AT_MOST_ONCE)
    }
    val i = indexOf(item)
    if (i == -1) return this.toList()

    val newList = toMutableList()
    newList[i] = replacement(item)
    return newList
}